
#ifndef _TTG_STRUCTURES_CASTABLE_OBJECT_
#define _TTG_STRUCTURES_CASTABLE_OBJECT_
#include "Common/Definitions.h"

#include <string>
#include "Common/Errors.h"

namespace ttg_internal
{
	namespace structures
	{
		class ObjectReleaser
		{
			public:
				virtual ~ObjectReleaser()
				{ /*nothing*/ }
		};
		template <class T>
		class ParametrizedObjectReleaser :public ObjectReleaser
		{
			private:
				T *data;
			public:
				inline ParametrizedObjectReleaser(T *data)
				{ this->data = data; }
				virtual ~ParametrizedObjectReleaser()
				{ SAFE_DELETE(data); }
		};
	}
}

namespace ttg
{
	namespace structures
	{
		//Contains object of any type as non-template.
		class CastableObject
		{
			private:
				void *data;
				std::string type_name;
				ttg_internal::structures::ObjectReleaser *releaser;
			private:
				//Hiding constructor.
				inline CastableObject(const CastableObject &)
				{ throw ttg::Error(ttg::Error::FATAL, "CastableObject", "CastableObject",
								   "Ata-ta!"); }
				//Hiding operator =.
				inline void operator =(const CastableObject &)
				{ throw ttg::Error(ttg::Error::FATAL, "CastableObject", "operator =",
								   "Ata-ta!"); }
				//Sets all fields by default values.
				inline CastableObject()
				{
					this->data = NULL;
					this->releaser = NULL;
				}
			public:
				//Casts contained object to requred type.
				//Note: in case of error throws ttg::Error().
				template <class T>
				inline T &cast()
				{
					if (type_name == typeid(T).name())
						return *(T *)data;
					else
						throw ttg::Error(ttg::Error::FATAL, "CastableObject", "cast",
										 "Wrong type");
				}
				//Creates object with required data.
				template <class T>
				static inline CastableObject *create(T data)
				{
					CastableObject *res = new CastableObject();
					res->data = new T(data);
					res->releaser
						= new ttg_internal::structures::ParametrizedObjectReleaser<T>((T *)res->data);
					res->type_name = typeid(T).name();
					return res;
				}
				//Creates object with required data.
				//Note: "data" should be available during object lifetime.
				template <class T>
				static inline CastableObject *create(T *data)
				{
					CastableObject *res = new CastableObject();
					res->data = data;
					res->releaser = NULL;
					res->type_name = typeid(T).name();
					return res;
				}
				~CastableObject()
				{ SAFE_DELETE(releaser); }
		};
	}
}

#endif
